ostree: Support for using EDITOR to fill commit subject/body
authorStef Walter <stefw@redhat.com>
Thu, 29 Aug 2013 15:23:20 +0000 (17:23 +0200)
committerStef Walter <stefw@redhat.com>
Thu, 29 Aug 2013 19:08:32 +0000 (21:08 +0200)
Behave similar to git when 'ostree commit' is run without
a --subject or --body. Bring up an editor. The first line becomes
the subject and following lines become the --body after an optional
blank line.

Use similar logic to git in determining EDITOR

https://bugzilla.gnome.org/show_bug.cgi?id=707063

Makefile-ostree.am
src/ostree/ot-builtin-commit.c
src/ostree/ot-editor.c [new file with mode: 0644]
src/ostree/ot-editor.h [new file with mode: 0644]

index 4cdde4684c23e8b2ed753e5a01ef46b334bae6cd..19bc85cc4236176522ac3ea35e6e4e7ccb283ae5 100644 (file)
@@ -44,6 +44,8 @@ ostree_SOURCES = src/ostree/main.c \
        src/ostree/ot-main.c \
        src/ostree/ot-dump.h \
        src/ostree/ot-dump.c \
+       src/ostree/ot-editor.c \
+       src/ostree/ot-editor.h \
        $(NULL)
 
 # Admin subcommand
index a62aa8bf04dbeb8f35da173a8cf23bdc0c270c3d..ace5aeb51020e022486f65890de2afbc225a4098 100644 (file)
@@ -23,6 +23,7 @@
 #include "config.h"
 
 #include "ot-builtins.h"
+#include "ot-editor.h"
 #include "ostree.h"
 #include "otutil.h"
 
@@ -134,6 +135,92 @@ commit_filter (OstreeRepo         *self,
   return OSTREE_REPO_COMMIT_FILTER_ALLOW;
 }
 
+static gboolean
+commit_editor (OstreeRepo     *repo,
+               const char     *branch,
+               char          **subject,
+               char          **body,
+               GCancellable   *cancellable,
+               GError        **error)
+{
+  const char *template =
+      "\n"
+      "# Please enter the commit message for your changes. The first line will\n"
+      "# become the subject, and the remainder the body. Lines starting\n"
+      "# with '#' will be ignored, and an empty message aborts the commit.\n"
+      "#\n"
+      "# Branch: %s\n";
+
+  gs_free char *input = NULL;
+  gs_free char *output = NULL;
+  gboolean ret = FALSE;
+  GString *bodybuf = NULL;
+  char **lines = NULL;
+  int i;
+
+  *subject = NULL;
+  *body = NULL;
+
+  input = g_strdup_printf (template, branch);
+
+  output = ot_editor_prompt (repo, input, cancellable, error);
+  if (output == NULL)
+    goto out;
+
+  lines = g_strsplit (output, "\n", -1);
+  for (i = 0; lines[i] != NULL; i++)
+    {
+      g_strchomp (lines[i]);
+
+      /* Lines starting with # are skipped */
+      if (lines[i][0] == '#')
+        continue;
+
+      /* Blank lines before body starts are skipped */
+      if (lines[i][0] == '\0')
+        {
+          if (!bodybuf)
+            continue;
+        }
+
+      if (!*subject)
+        {
+          *subject = g_strdup (lines[i]);
+        }
+      else if (!bodybuf)
+        {
+          bodybuf = g_string_new (lines[i]);
+        }
+      else
+        {
+          g_string_append_c (bodybuf, '\n');
+          g_string_append (bodybuf, lines[i]);
+        }
+    }
+
+  if (!*subject)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Aborting commit due to empty commit subject.");
+      goto out;
+    }
+
+  if (bodybuf)
+    {
+      *body = g_string_free (bodybuf, FALSE);
+      g_strchomp (*body);
+      bodybuf = NULL;
+    }
+
+  ret = TRUE;
+
+out:
+  g_strfreev (lines);
+  if (bodybuf)
+    g_string_free (bodybuf, TRUE);
+  return ret;
+}
+
 gboolean
 ostree_builtin_commit (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error)
 {
@@ -179,13 +266,6 @@ ostree_builtin_commit (int argc, char **argv, OstreeRepo *repo, GCancellable *ca
       goto out;
     }
 
-  if (!opt_subject)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "A subject must be specified with --subject");
-      goto out;
-    }
-
   if (opt_owner_uid >= 0 || opt_owner_gid >= 0 || opt_statoverride_file != NULL
       || opt_no_xattrs)
     {
@@ -205,6 +285,19 @@ ostree_builtin_commit (int argc, char **argv, OstreeRepo *repo, GCancellable *ca
         goto out;
     }
 
+  if (!opt_subject && !opt_body)
+    {
+      if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error))
+        goto out;
+    }
+
+  if (!opt_subject)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "A subject must be specified with --subject");
+      goto out;
+    }
+
   if (!ostree_repo_prepare_transaction (repo, opt_link_checkout_speedup, NULL, cancellable, error))
     goto out;
 
diff --git a/src/ostree/ot-editor.c b/src/ostree/ot-editor.c
new file mode 100644 (file)
index 0000000..e1ca1a1
--- /dev/null
@@ -0,0 +1,110 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Stef Walter <stefw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#include "config.h"
+
+#include "ot-editor.h"
+#include "libgsystem.h"
+
+#include <sys/wait.h>
+#include <string.h>
+
+#ifndef DEFAULT_EDITOR
+#define DEFAULT_EDITOR "vi"
+#endif
+
+/* Logic pulled from git */
+
+static const char *
+get_editor (void)
+{
+  const char *editor = g_getenv ("OSTREE_EDITOR");
+  const char *terminal = g_getenv ("TERM");
+  int terminal_is_dumb = !terminal || g_str_equal (terminal, "dumb");
+
+  if (!editor && !terminal_is_dumb)
+    editor = g_getenv ("VISUAL");
+  if (!editor)
+    editor = g_getenv ("EDITOR");
+
+  if (!editor && terminal_is_dumb)
+    return NULL;
+
+  if (!editor)
+    editor = DEFAULT_EDITOR;
+
+  return editor;
+}
+
+char *
+ot_editor_prompt (OstreeRepo *repo,
+                  const char *input,
+                  GCancellable *cancellable,
+                  GError **error)
+{
+  gs_unref_object GSSubprocessContext *ctx = NULL;
+  gs_unref_object GSSubprocess *proc = NULL;
+  gs_unref_object GFile *file = NULL;
+  gs_unref_object GFileIOStream *io = NULL;
+  GOutputStream *output;
+  const char *editor;
+  char *ret = NULL;
+
+  editor = get_editor ();
+  if (editor == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Terminal is dumb, but EDITOR unset");
+      goto out;
+    }
+
+  file = g_file_new_tmp (NULL, &io, error);
+  if (file == NULL)
+    goto out;
+
+  output = g_io_stream_get_output_stream (G_IO_STREAM (io));
+  if (!g_output_stream_write_all (output, input, strlen (input), NULL, cancellable, error) ||
+      !g_io_stream_close (G_IO_STREAM (io), cancellable, error))
+    goto out;
+
+  ctx = gs_subprocess_context_newv (editor, gs_file_get_path_cached (file), NULL);
+  gs_subprocess_context_set_stdin_disposition (ctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT);
+  gs_subprocess_context_set_stdout_disposition (ctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT);
+  gs_subprocess_context_set_stderr_disposition (ctx, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT);
+
+  proc = gs_subprocess_new (ctx, cancellable, error);
+  if (proc == NULL)
+    goto out;
+
+  if (!gs_subprocess_wait_sync_check (proc, cancellable, error))
+    {
+      g_prefix_error (error, "There was a problem with the editor '%s'.", editor);
+      goto out;
+    }
+
+  ret = gs_file_load_contents_utf8 (file, cancellable, error);
+
+out:
+  if (file)
+    (void )g_file_delete (file, NULL, NULL);
+  return ret;
+}
diff --git a/src/ostree/ot-editor.h b/src/ostree/ot-editor.h
new file mode 100644 (file)
index 0000000..81b6361
--- /dev/null
@@ -0,0 +1,30 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Stef Walter <stefw@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+#include "ostree.h"
+
+char *  ot_editor_prompt    (OstreeRepo *repo, const char *input,
+                             GCancellable *cancellable, GError **error);